#include "GBoxAligned.h"
#include "GBoxOriented.h"

namespace GMath
{

	const GBoxAligned GBoxAligned::UnitBox = GBoxAligned(GVector3(-0.5f, -0.5f, -0.5f),
															GVector3( 0.5f,  0.5f,  0.5f));

	GBoxAligned::GBoxAligned()
	{
		Min = -GVector3::One / 2.0f;
		Max = GVector3::One / 2.0f;
	}

	GBoxAligned::GBoxAligned(const GVector3& TheMin, const GVector3& TheMax)
	{
		Create(TheMin, TheMax);
	}

	GBoxAligned::GBoxAligned(const GVector3& Center, float Width, float Height, float Depth)
	{
		Create(Center, Width, Height, Depth);
	}

	void GBoxAligned::Create(const GVector3& TheMin, const GVector3& TheMax)
	{
		Min = TheMin;
		Max = TheMax;
	}

	void GBoxAligned::Create(const GVector3& Center, float Width, float Height, float Depth)
	{
		GVector3 HalfSize(Width / 2.0f, Height / 2.0f, Depth / 2.0f);

		Min = Center - HalfSize;
		Max = Center + HalfSize;
	}
 
	void GBoxAligned::GetNormal(OUTPUT GVector3& OutNormal, const GVector3& SurfacePoint) const
	{
		//!++ YAZILACAK
		OutNormal = GVector3::Zero;
	}

	float GBoxAligned::Volume() const
	{
		GVector3 Dim = Max - Min;
		return Dim.x * Dim.y * Dim.z;
	}

	float GBoxAligned::SurfaceArea() const
	{
		GVector3 Dim = Max - Min;
		return 2.0f * (Dim.x * Dim.y + Dim.x * Dim.z + Dim.y * Dim.z);
	}

	GVector3 GBoxAligned::GetCenter() const
	{
		GVector3 HalfDim = (Max - Min) / 2.0f;
		return Min + HalfDim;
	}

	GVector3 GBoxAligned::GetDimension() const
	{
		return Max - Min;
	}

	void GBoxAligned::GetPlanes(OUTPUT GPlane PlaneArray[G_BP_COUNT]) const
	{
		GVector3 Center = GetCenter();
		GVector3 HalfDim = GetDimension() / 2.0f;

		GVector3 PointRight = Center + GVector3::UnitX * HalfDim.x;
		GVector3 PointBottom = Center - GVector3::UnitY * HalfDim.y;
		GVector3 PointLeft = Center - GVector3::UnitX * HalfDim.x;
		GVector3 PointTop = Center + GVector3::UnitY * HalfDim.y;
		GVector3 PointNear = Center - GVector3::UnitZ * HalfDim.z;
		GVector3 PointFar = Center + GVector3::UnitZ * HalfDim.z;

		PlaneArray[G_BP_RIGHT]	= GPlane(PointRight, -GVector3::UnitX);
		PlaneArray[G_BP_BOTTOM] = GPlane(PointBottom, GVector3::UnitY);
		PlaneArray[G_BP_LEFT]	= GPlane(PointLeft, GVector3::UnitX);
		PlaneArray[G_BP_TOP]	= GPlane(PointTop, -GVector3::UnitY);
		PlaneArray[G_BP_NEAR]	= GPlane(PointNear, GVector3::UnitZ);
		PlaneArray[G_BP_FAR]	= GPlane(PointFar, -GVector3::UnitZ);
	}
	
	void GBoxAligned::GetCorners(OUTPUT GVector3 Arr[G_BC_COUNT]) const
	{
		GVector3 Center = GetCenter();
		GVector3 HalfDimension = GetDimension() / 2.0f;
		GVector3 WidthVec(GetRight() * HalfDimension.x);
		GVector3 HeightVec(GetUp() * HalfDimension.y);
		GVector3 DepthVec(GetFront() * HalfDimension.z);

		Arr[G_BC_NEAR_BOTTOM_LEFT]	= Center - DepthVec - HeightVec - WidthVec;
		Arr[G_BC_NEAR_BOTTOM_RIGHT] = Center - DepthVec - HeightVec + WidthVec;
		Arr[G_BC_NEAR_TOP_LEFT]		= Center - DepthVec + HeightVec - WidthVec;
		Arr[G_BC_NEAR_TOP_RIGHT]	= Center - DepthVec + HeightVec + WidthVec;
		Arr[G_BC_FAR_BOTTOM_LEFT]	= Center + DepthVec - HeightVec - WidthVec;
		Arr[G_BC_FAR_BOTTOM_RIGHT]	= Center + DepthVec - HeightVec + WidthVec;
		Arr[G_BC_FAR_TOP_LEFT]		= Center + DepthVec + HeightVec - WidthVec;
		Arr[G_BC_FAR_TOP_RIGHT]		= Center + DepthVec + HeightVec + WidthVec;
	}

	#define		G_BOX_X_AXIS		(GUInt32)0x01
	#define		G_BOX_Y_AXIS		(GUInt32)0x02
	#define		G_BOX_Z_AXIS		(GUInt32)0x04

	GVector3 GBoxAligned::GetCorner(GBoxCorner Corner) const
	{
		return GVector3((Corner & G_BOX_X_AXIS) ? Max.x : Min.x, 
						(Corner & G_BOX_Y_AXIS) ? Max.y : Min.y, 
						(Corner & G_BOX_Z_AXIS) ? Max.z : Min.z);
	}

	const GVector3 GBoxAligned::GetRight() const
	{
		return GVector3::UnitX;
	}

	const GVector3 GBoxAligned::GetUp() const
	{
		return GVector3::UnitY;
	}

	const GVector3 GBoxAligned::GetFront() const
	{
		return GVector3::UnitZ;
	}

	void GBoxAligned::Combine(const GBoxAligned& Box)
	{
		GBoxAligned Temp(*this);

		GBoxAligned::Combine(*this, Temp, Box);
	}

	void GBoxAligned::Combine(OUTPUT GBoxAligned& OutBox, const GBoxAligned& Box1, const GBoxAligned& Box2)
	{
		OutBox.Min = Box1.Min;
		OutBox.Max = Box1.Max;

		if (OutBox.Min.x > Box2.Min.x)
		{
			OutBox.Min.x = Box2.Min.x;
		}
		else if (OutBox.Min.x < Box2.Max.x)
		{
			OutBox.Min.x = Box2.Max.x;
		}
		if (OutBox.Min.y > Box2.Min.y)
		{
			OutBox.Min.y = Box2.Min.y;
		}
		else if (OutBox.Min.y < Box2.Max.y)
		{
			OutBox.Min.y = Box2.Max.y;
		}
		if (OutBox.Min.z > Box2.Min.z)
		{
			OutBox.Min.z = Box2.Min.z;
		}
		else if (OutBox.Min.z < Box2.Max.z)
		{
			OutBox.Min.z = Box2.Max.z;
		}
	}

	void GBoxAligned::Convert(OUTPUT GBoxOriented& OutOBox, const GBoxAligned& Box)
	{
		OutOBox.Create(Box.GetCenter(), Box.GetDimension(), Box.GetFront(), Box.GetRight(), Box.GetUp());
	}

	void GBoxAligned::Convert(OUTPUT GBoxAligned& OutBox, const GBoxOriented& Box)
	{
		GVector3 Corners[G_BC_COUNT];
		Box.GetCorners(Corners);
		
		// iterate all corners of oriented box and find min max for each axis.
		GVector3 Min = Corners[0]; 
		GVector3 Max = Corners[0];
		for (GSize I = 1; I < G_BC_COUNT; ++I)
		{
			if (Corners[I].x < Min.x)
			{
				Min.x = Corners[I].x;
			}
			else if (Corners[I].x > Max.x)
			{
				Max.x = Corners[I].x;
			}

			if (Corners[I].y < Min.y)
			{
				Min.y = Corners[I].y;
			}
			else if (Corners[I].y > Max.y)
			{
				Max.y = Corners[I].y;
			}

			if (Corners[I].z < Min.z)
			{
				Min.z = Corners[I].z;
			}
			else if (Corners[I].z > Max.z)
			{
				Max.z = Corners[I].z;
			}
		}

		OutBox.Create(Min, Max);
	}


	const GBoxAligned& GBoxAligned::operator = (const GBoxAligned& Box)
	{
		Min = Box.Min;
		Max = Box.Max;

		return *this;
	}
}